﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using GalaSoft.MvvmLight;
using MusicExplorer.Data;
using MusicExplorer.Helper;
using MusicExplorer.Internals;
using MusicExplorer.WebRepository.Interface;
using MusicExplorer.Wrappers;

namespace MusicExplorer.ViewModel
{
    public sealed class TrackListViewModel : ViewModelBase, IDisposable
    {
        private MainWindowViewModel m_parent;
        public MainWindowViewModel Parent { get { return m_parent; } }

        public TrackListViewModel(MainWindowViewModel parent)
        {
            m_parent = parent;

            m_webClientTracksLoading = new WebClient();
            TokenTracksLoading = new CancellationTokenSource();
            m_tracks = new ObservableCollection<TrackWrapper>();

            m_trackListModes = new ObservableCollection<TrackListModeWrapper>();
            foreach (var e in Enum.GetValues(typeof(TrackListMode)))
                m_trackListModes.Add(new TrackListModeWrapper((TrackListMode)e));
        }

        #region TRACKS LOADING TASK

        private readonly WebClient m_webClientTracksLoading;
        public WebClient WebClientTracksLoading { get { return m_webClientTracksLoading; } }

        private CancellationTokenSource m_tokenTracksLoading;
        public CancellationTokenSource TokenTracksLoading
        {
            get { return m_tokenTracksLoading; }
            set { m_tokenTracksLoading = value; }
        }

        private Task m_taskTracksLoading;
        public Task TaskTracksLoading
        {
            get { return m_taskTracksLoading; }
            set { m_taskTracksLoading = value; }
        }

        private int m_currentPageTracks;
        public int CurrentPageTracks
        {
            get { return m_currentPageTracks; }
            set { m_currentPageTracks = value; }
        }

        #endregion

        #region PROPERTIES

        private TrackWrapper m_selectedTrack;
        public TrackWrapper SelectedTrack
        {
            get { return m_selectedTrack; }
            set
            {
                if (m_selectedTrack != value)
                {
                    if(m_selectedTrack != null) m_selectedTrack.IsSelected = false;
                    m_selectedTrack = value;
                    RaisePropertyChanged("SelectedTrack");

                    if (m_selectedTrack != null)
                    {
                        m_selectedTrack.IsSelected = true;
                        Parent.Player.PlayTrack(m_selectedTrack);
                    }
                }
            }
        }

        private ObservableCollection<TrackWrapper> m_tracks;
        public ObservableCollection<TrackWrapper> Tracks
        {
            get { return m_tracks; }
            set
            {
                if (m_tracks != value)
                {
                    m_tracks = value;
                    RaisePropertyChanged("Tracks");
                }
            }
        }

        private string m_trackListTitle;
        public string TrackListTitle
        {
            get { return m_trackListTitle; }
            set
            {
                if (m_trackListTitle != value)
                {
                    m_trackListTitle = value;
                    RaisePropertyChanged("TrackListTitle");
                }
            }
        }

        #endregion

        #region FUNCTIONS

        private bool m_nothingToLoad;
        private bool LoadTracksFromCurrentMode()
        {
            Parent.Log.SetLog("Loading of " + Parent.WebRepositoryTree.SelectedWebRepository.DisplayName +
                (Parent.DetailTree.SelectedItem != null ? " - " + Parent.DetailTree.SelectedItem.DisplayName : string.Empty) + " tracks...",
                LogTypeEnum.Loading);

            if (m_tokenTracksLoading.IsCancellationRequested)
                return false;

            Task<List<TRACK>> task = null;

            if (m_loadingMode == LoadingMode.DetailItem)
            {
                switch (m_currentEntityToLoad)
                {
                    case EntityToLoad.Group:
                        switch (m_currentTrackListMode.TrackListMode)
                        {
                            case TrackListMode.AllTracks:
                                task = ((IGroupTrackListingWebRepository)Parent.WebRepositoryTree.SelectedWebRepository.WebRepository)
                                    .GetTracksFromGroupAsync(WebClientTracksLoading, m_tokenTracksLoading.Token, Parent.DetailTree.SelectedGroup.Entity as MusicExplorer.Data.GROUP, CurrentPageTracks);
                                break;
                            case TrackListMode.TopTracks:
                                task = ((IGroupTopTrackListingWebRepository)Parent.WebRepositoryTree.SelectedWebRepository.WebRepository)
                                    .GetTopTracksFromGroupAsync(WebClientTracksLoading, m_tokenTracksLoading.Token, Parent.DetailTree.SelectedGroup.Entity as MusicExplorer.Data.GROUP, CurrentPageTracks);
                                break;
                        }
                        break;
                    case EntityToLoad.Artist:
                        switch (m_currentTrackListMode.TrackListMode)
                        {
                            case TrackListMode.AllTracks:
                                task = ((IArtistTrackListingWebRepository)Parent.WebRepositoryTree.SelectedWebRepository.WebRepository)
                                    .GetTracksFromArtistAsync(WebClientTracksLoading, m_tokenTracksLoading.Token, Parent.DetailTree.SelectedArtist.Entity as MusicExplorer.Data.ARTIST, CurrentPageTracks);
                                break;
                            case TrackListMode.TopTracks:
                                task = ((IArtistTopTrackListingWebRepository)Parent.WebRepositoryTree.SelectedWebRepository.WebRepository)
                                    .GetTopTracksFromArtistAsync(WebClientTracksLoading, m_tokenTracksLoading.Token, Parent.DetailTree.SelectedArtist.Entity as MusicExplorer.Data.ARTIST, CurrentPageTracks);
                                break;
                        }
                        break;
                    case EntityToLoad.Label:
                        switch (m_currentTrackListMode.TrackListMode)
                        {
                            case TrackListMode.AllTracks:
                                throw new NotSupportedException();
                                break;
                            case TrackListMode.TopTracks:
                                throw new NotSupportedException();
                                break;
                        }
                        break;
                }
            }
            else if (m_loadingMode == LoadingMode.WebRepository)
            {
                switch (m_currentTrackListMode.TrackListMode)
                {
                    case TrackListMode.AllTracks:
                        task = ((ITrackListingWebRepository)Parent.WebRepositoryTree.SelectedWebRepository.WebRepository)
                            .GetTracksAsync(WebClientTracksLoading, m_tokenTracksLoading.Token, CurrentPageTracks);
                        break;
                    case TrackListMode.TopTracks:
                        throw new NotSupportedException();
                        break;
                }
            }
            else throw new NotSupportedException();

            try
            {
                task.Wait();
            }
            catch (Exception ex)
            {
                //if (!(ex is AggregateException || ex is OperationCanceledException))
                Parent.Log.LogError(ex);
                return false;
            }

            if (m_tokenTracksLoading.IsCancellationRequested)
                return false;

            if (task.Result != null && task.Result.Count > 0)
            {
                MusicExplorerHelper.ExecuteInUIThread(() =>
                {
                    foreach (var track in task.Result)
                    {
                        var t = new TrackWrapper(track);
                        Tracks.Add(t);
                    }
                    LaunchTaskFromScrollViewerState();
                });
            }
            else m_nothingToLoad = true;

            Parent.Log.ResetLog();
            return true;
        }

        #endregion

        #region SCROLL VIEWER

        private ScrollViewerState m_scrollViewerState;
        public ScrollViewerState ScrollViewerState
        {
            get { return m_scrollViewerState; }
            set
            {
                m_scrollViewerState = value;
                if (TaskTracksLoading != null && TaskTracksLoading.IsCompleted)
                    LaunchTaskFromScrollViewerState();
            }
        }

        public void LaunchTaskFromScrollViewerState()
        {
            if (m_scrollViewerState.HeightArea > 0 && !m_nothingToLoad)
            {
                if (m_scrollViewerState.ScrollableHeight == 0 ||
                    m_scrollViewerState.ScrollableHeight - m_scrollViewerState.CurrentScroll < 10)
                {
                    CurrentPageTracks++;
                    m_taskTracksLoading = Task.Factory.StartNew(() => LoadTracksFromCurrentMode());
                }
            }
        }

        #endregion

        #region MODE

        private LoadingMode m_loadingMode;

        private TrackListModeWrapper m_currentTrackListMode;
        public TrackListModeWrapper CurrentTrackListMode
        {
            get { return m_currentTrackListMode; }
            set
            {
                if (m_currentTrackListMode != value)
                {
                    m_currentTrackListMode = value;
                    RaisePropertyChanged("CurrentTrackListMode");
                }

                if(m_currentTrackListMode != null) NewLoading();
            }
        }

        private EntityToLoad m_currentEntityToLoad;
        public EntityToLoad CurrentEntityToLoad
        {
            get { return m_currentEntityToLoad; }
            set
            {
                if (m_currentEntityToLoad != value)
                {
                    m_currentEntityToLoad = value;
                    RaisePropertyChanged("CurrentEntityToLoad");
                }
            }
        }

        private void NewLoading()
        {
            m_nothingToLoad = false;
            TokenTracksLoading.Cancel();
            WebClientTracksLoading.CancelAsync();
            Tracks.Clear();

            if (TaskTracksLoading != null)
            {
                // This task has cancel pending and stop in few ms
                TaskTracksLoading.Wait();
            }

            CurrentPageTracks = 1;
            TokenTracksLoading = new CancellationTokenSource();

            switch (m_currentTrackListMode.TrackListMode)
            {
                case TrackListMode.TopTracks:
                    switch(m_loadingMode)
                    {
                        case LoadingMode.DetailItem:
                            TrackListTitle = Parent.WebRepositoryTree.SelectedWebRepository.DisplayName + " - " + Parent.DetailTree.SelectedItem.DisplayName + " - Top current tracks";
                            break;
                        case LoadingMode.WebRepository:
                            TrackListTitle = Parent.WebRepositoryTree.SelectedWebRepository.DisplayName + " - Top current tracks";
                            break;
                    }
                    break;
                case TrackListMode.AllTracks:
                    switch(m_loadingMode)
                    {
                        case LoadingMode.DetailItem:
                            TrackListTitle = Parent.WebRepositoryTree.SelectedWebRepository.DisplayName + " - " + Parent.DetailTree.SelectedItem.DisplayName + " - Tracks of all time";
                            break;
                        case LoadingMode.WebRepository:
                            TrackListTitle = Parent.WebRepositoryTree.SelectedWebRepository.DisplayName + " - Tracks of all time";
                            break;
                    }
                    break;
                default:
                    throw new NotSupportedException();
            }

            m_taskTracksLoading = Task.Factory.StartNew(() => LoadTracksFromCurrentMode());
        }

        private readonly ObservableCollection<TrackListModeWrapper> m_trackListModes;
        public ObservableCollection<TrackListModeWrapper> TrackListModes { get { return m_trackListModes; } }

        #endregion

        #region LOADING

        public void Load(DetailTreeItemWrapper item)
        {
            if (item == null) return;

            m_loadingMode = LoadingMode.DetailItem;
            Parent.Player.Reset();
            TrackListModes.Clear();

            if (item.Entity is GROUP)
            {
                CurrentEntityToLoad = EntityToLoad.Group;

                if (Parent.DetailTree.SelectedWebRepository.WebRepository is IGroupTopTrackListingWebRepository)
                    TrackListModes.Add(new TrackListModeWrapper(TrackListMode.TopTracks));
                if (Parent.DetailTree.SelectedWebRepository.WebRepository is IGroupTrackListingWebRepository)
                    TrackListModes.Add(new TrackListModeWrapper(TrackListMode.AllTracks));
            }
            else if (item.Entity is ARTIST)
            {
                CurrentEntityToLoad = EntityToLoad.Artist;

                if (Parent.DetailTree.SelectedWebRepository.WebRepository is IArtistTopTrackListingWebRepository)
                    TrackListModes.Add(new TrackListModeWrapper(TrackListMode.TopTracks));
                if (Parent.DetailTree.SelectedWebRepository.WebRepository is IArtistTrackListingWebRepository)
                    TrackListModes.Add(new TrackListModeWrapper(TrackListMode.AllTracks));
            }
            else throw new NotImplementedException();

            if (TrackListModes.Count == 0) throw new NotSupportedException();

            CurrentTrackListMode = TrackListModes[0];
        }

        public void Load(WebRepositoryWrapper repo)
        {
            if (repo == null) return;

            m_loadingMode = LoadingMode.WebRepository;

            Parent.Player.Reset();
            TrackListModes.Clear();

            TrackListModes.Add(new TrackListModeWrapper(TrackListMode.AllTracks));
            CurrentTrackListMode = TrackListModes[0];
        }

        #endregion

        public void Dispose()
        {
            if (m_webClientTracksLoading != null) m_webClientTracksLoading.Dispose();
        }
    }
}
